package com.childenglish.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 限流过滤器 - 防止DoS攻击
 * 基于IP地址的请求频率限制
 */
public class RateLimitFilter implements Filter {

    private static final Logger logger = LoggerFactory.getLogger(RateLimitFilter.class);

    // 每个IP的最大请求数
    private static final int MAX_REQUESTS_PER_MINUTE = 60;
    private static final int MAX_REQUESTS_PER_SECOND = 10;

    // 时间窗口（毫秒）
    private static final long TIME_WINDOW_MINUTE = 60 * 1000;
    private static final long TIME_WINDOW_SECOND = 1000;

    // 存储每个IP的请求记录
    private final ConcurrentHashMap<String, RequestRecord> requestRecords = new ConcurrentHashMap<>();

    // 不需要限流的路径
    private static final String[] EXCLUDED_PATHS = {
        "/health",
        "/file/health"
    };

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        // 检查是否在排除列表中
        if (isExcludedPath(httpRequest.getRequestURI())) {
            chain.doFilter(request, response);
            return;
        }

        String clientIp = getClientIp(httpRequest);
        RequestRecord record = requestRecords.computeIfAbsent(clientIp, k -> new RequestRecord());

        long currentTime = System.currentTimeMillis();

        // 清理过期记录
        record.cleanup(currentTime);

        // 检查秒级限流
        if (record.getSecondCount() >= MAX_REQUESTS_PER_SECOND) {
            logger.warn("IP {} 触发秒级限流: {} 请求/秒", clientIp, record.getSecondCount());
            httpResponse.setStatus(429); // SC_TOO_MANY_REQUESTS = 429
            httpResponse.setContentType("application/json;charset=UTF-8");
            httpResponse.getWriter().write("{\"code\":429,\"message\":\"请求过于频繁，请稍后再试\"}");
            return;
        }

        // 检查分钟级限流
        if (record.getMinuteCount() >= MAX_REQUESTS_PER_MINUTE) {
            logger.warn("IP {} 触发分钟级限流: {} 请求/分钟", clientIp, record.getMinuteCount());
            httpResponse.setStatus(429); // SC_TOO_MANY_REQUESTS = 429
            httpResponse.setContentType("application/json;charset=UTF-8");
            httpResponse.getWriter().write("{\"code\":429,\"message\":\"请求过于频繁，请稍后再试\"}");
            return;
        }

        // 记录请求
        record.recordRequest(currentTime);

        chain.doFilter(request, response);
    }

    /**
     * 获取客户端IP地址
     */
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // 处理多个IP的情况，取第一个
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }
        return ip;
    }

    /**
     * 检查路径是否在排除列表中
     */
    private boolean isExcludedPath(String path) {
        for (String excludedPath : EXCLUDED_PATHS) {
            if (path.startsWith(excludedPath)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 请求记录类
     */
    private static class RequestRecord {
        private final AtomicInteger secondCount = new AtomicInteger(0);
        private final AtomicInteger minuteCount = new AtomicInteger(0);
        private long secondWindowStart = System.currentTimeMillis();
        private long minuteWindowStart = System.currentTimeMillis();

        /**
         * 记录请求
         */
        public void recordRequest(long currentTime) {
            // 秒级计数
            if (currentTime - secondWindowStart < TIME_WINDOW_SECOND) {
                secondCount.incrementAndGet();
            } else {
                secondCount.set(1);
                secondWindowStart = currentTime;
            }

            // 分钟级计数
            if (currentTime - minuteWindowStart < TIME_WINDOW_MINUTE) {
                minuteCount.incrementAndGet();
            } else {
                minuteCount.set(1);
                minuteWindowStart = currentTime;
            }
        }

        /**
         * 清理过期记录
         */
        public void cleanup(long currentTime) {
            // 重置秒级计数
            if (currentTime - secondWindowStart >= TIME_WINDOW_SECOND) {
                secondCount.set(0);
                secondWindowStart = currentTime;
            }

            // 重置分钟级计数
            if (currentTime - minuteWindowStart >= TIME_WINDOW_MINUTE) {
                minuteCount.set(0);
                minuteWindowStart = currentTime;
            }
        }

        public int getSecondCount() {
            return secondCount.get();
        }

        public int getMinuteCount() {
            return minuteCount.get();
        }
    }
}

